import java.util.LinkedList;
import java.util.Stack;

public class LLParser {

	Grammar g;

	LinkedList<RulePiece> input;
	Stack<RulePiece> ruleStack;

	static String END_MARK = "\0";
	static String END_TYPE = "\0";

	public LLParser(Grammar g) {

		input = new LinkedList<RulePiece>();
		ruleStack = new Stack<RulePiece>();

		addToStack(END_TYPE, END_MARK, 0);

		this.g = g;

		addToStack("$RULE", g.getRule(0).getName(), 1); // Add starting rule to
														// stack

	}

	public boolean step(boolean print) {

		RulePiece r = ruleStack.pop();

		RulePiece p;

		if (r.getType().equals(END_TYPE)) {

			if(!input.isEmpty()){
				System.out.println("Error: extranious code at end of program:");
				while(!input.isEmpty()){
					System.out.print(input.pop().getContent());
				}
				return false;
			}
			
			
			System.out.println("Success!");
			return false;
		}

		if (r.getType().equals("$RULE")) {

			if (print)
				System.out.println("Popped rule " + r.getContent());

			RulePiece in = input.peek();

			GrammarRule n = g.findGrammarRule(r.getContent(), in); // Exchange
																	// rule for
																	// rule
																	// matching
																	// with
			// first set
			if (n == null) {
				boolean b = g.findElipson(r.getContent());

				if (!b) {
					System.out.println("Error: No rule matching grammar rule "
							+ r.getContent() + " starting with " + in);
					return false;
				}

			} else
				addToStack(n, print);

		}
		if (r.getType().equals("$TERMINAL")) {
			p = input.pop();
			if (r.getContent().equalsIgnoreCase(p.getContent())) { // Read
																	// input
																	// off

				if (print)
					System.out.println("Read \"" + p.getContent() + "\"");

			} else {
				System.out.println("Error: Expecting " + r.getContent()
						+ " and got " + p.getContent());
				System.out.println("Line Number: " + p.getLineNumber()
						+ " in the script");
				System.out.println("Line Number: " + r.getLineNumber()
						+ " in the grammar");
				return false;
			}
		}

		if (r.getType().equals("$SPEC")) {
			p = input.pop();
			if (r.getContent().equalsIgnoreCase(ruleToUpper(p.getType()))) {

				if (print)
					System.out.println("Read \"" + p.getContent() + "\"");

			} else {

				System.out.println("Error: Expecting " + r.getContent()
						+ " and got " + ruleToUpper(p.getType()));
				System.out.println("Line Number: " + p.getLineNumber()
						+ " in the script");
				System.out.println("Line Number: " + r.getLineNumber()
						+ " in the grammar");

				return false;
			}

		}

		return true;

	}

	private void addToStack(GrammarRule n, boolean print) {

		if (print)
			System.out.println("Added \"" + n.piecesToString()
					+ "\" to the stack");

		for (int i = n.numPieces() - 1; i >= 0; i--) {

			addToStack(n.getPiece(i));

		}

	}

	public void addInput(String s, int lineNumber) {

		while (s.indexOf('\n') > 0) {

			addInput(s.substring(0, s.indexOf(' ')),
					s.substring(s.indexOf(' ') + 1, s.indexOf('\n')+1), lineNumber);
			
			if(s.indexOf('\n')+1==s.length()){
				s="";
			}
			else{
				s=s.substring(s.indexOf('\n')+1, s.length());
			}
		}
	}

	public void addInput(String type, String content, int lineNumber) {

		while (content.charAt(content.length() - 1) == ' ') {
			content = content.substring(0, content.length() - 1);
		}

		addInput(new RulePiece(type, content, lineNumber));

	}

	public void addInput(RulePiece rp) {
		input.add(rp);
	}

	public void addToStack(String type, String content, int lineNumber) {
		addToStack(new RulePiece(type, content, lineNumber));
	}

	public void addToStack(RulePiece rp) {
		ruleStack.add(rp);
	}

	public String ruleToUpper(String s) {
		s = s.substring(1);
		return s;
	}

}
